package socket

import (
	"context"
	"encoding/json"
	"feishup2pclient/data"
	"feishup2pclient/msg"
	MSG "feishup2pclient/msg"
	"feishup2pclient/p2p"
	"feishup2pclient/utils"
	"feishup2pclient/wg"
	"fmt"
	"net/http"
	"net/url"
	"runtime"
	"time"

	"github.com/gorilla/websocket"
	"github.com/libp2p/go-libp2p-core/host"
	"github.com/libp2p/go-libp2p-core/peer"
)

var (
	token      data.Token
	serverConn *data.Connetion
	relayId    peer.ID
	relayHost  host.Host = nil
	pongWait             = 30 * time.Second
	pingPeriod           = (pongWait * 8) / 10
	writeWait            = 10 * time.Second
	RelayAddr            = "/ip4/115.227.19.84/tcp/8001"
	RelayId              = "QmVSfyZ91nMMQWBdeyrvjpDaA8rCoXX6nshTeUu91znvBs"

	ctx          = context.Background()
	SocketRun    = false
	closeCh      = make(chan bool, 0)
	timeOutCount = 0
	Socket_exit  = true
)

func StartSocket(ctx context.Context, t data.Token) {
	token = t
	go p2p.StartP2P(ctx, token.Cn, token.CIp)
	err, osName := wg.GetOsName_windows()
	if err != nil {
		msg.ErrCheck(err.Error())
		return
	}
	data.SelfOsName = osName
	// for {
	// 	select {
	// 	case <-time.Tick(time.Second * 3):

	// 	}
	// }
	ticker := time.NewTicker(time.Second * 5)
	go func() {
		for {
			select {
			case fails, ok := <-p2p.FailsCh:
				if ok {
					if err := SendJsonToMaster(data.P2PSocketParams{MType: `fails`, Peers: fails}, true); err != nil {
						msg.SendMessage(fmt.Sprintf("上送连接失败信息失败:%s", err.Error()))
					}
				}
			}
		}
	}()
	go func() {
		for {
			select {
			case <-ctx.Done():
				fmt.Println("Exiting goroutine")
				return
			default:
				if serverConn != nil {
					serverConn.Con.SetReadDeadline(time.Now().Add(pongWait))
					transmitParams := data.TransmitParams{}
					err := serverConn.Con.ReadJSON(&transmitParams)
					if err != nil {
						MSG.SendMessage(fmt.Sprintf("read socket server err: ", err.Error()))
						for i := 3; i > 0; i-- {
							u := url.URL{Scheme: "ws", Host: token.Server, Path: "/p2pWs"}
							MSG.SendMessage(fmt.Sprintf("p2p socket 尝试连接:%s", u.String()))
							header := http.Header{}
							header.Add(`p2p-cid`, token.Cid)
							header.Add(`p2p-aid`, token.Aid)
							header.Add(`p2p-pid`, data.SelfP2PID) //libp2p id
							header.Add(`p2p-on`, osName)
							header.Add(`p2p-cp`, fmt.Sprintf("%d", data.ControlPort))
							c, _, err := websocket.DefaultDialer.Dial(u.String(), header)
							if err != nil {
								MSG.SendMessage(fmt.Sprintf("reconnect socket server err : %s", err.Error()))
							} else {
								serverConn.Con = c
								break
							}
						}
						continue
					}
					MSG.SendMessage(fmt.Sprintf(`接收server消息原始数据[%+v]`, transmitParams))
					params, err := decrypt(transmitParams.EncryptionData, utils.DefaultAesKey)
					if err != nil {
						MSG.SendMessage(fmt.Sprintf("p2p socket 接收server消息再次解密失败:%s", err))
						continue
					}
					str, err := json.Marshal(params)
					if err != nil {
						MSG.SendMessage(fmt.Sprintf("p2p socket 接收server消息json[%s]", str))
					} else {
						MSG.SendMessage(fmt.Sprintf("p2p socket 接收server消息[%+v]", params))
					}
					switch params.MType {
					case `peer`:
						handlerPeer(params)
					case `relay`:
						handlerRelayInfo(params)
					case `pong`:
						runtime.Gosched()
					}
				}
			}
		}
	}()
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			if !SocketRun {
				if len(data.SelfP2PID) != 0 && len(data.SelfP2PIP) != 0 {
					SocketRun = true
					startWs(ctx, osName)
				}
			}
		case <-ctx.Done():
			ticker.Stop()
			return
		}
	}
}

func startWs(ctx context.Context, osName string) {
	u := url.URL{Scheme: "ws", Host: token.Server, Path: "/p2pWs"}
	MSG.SendMessage(fmt.Sprintf("p2p socket 尝试连接:%s", u.String()))
	header := http.Header{}
	header.Add(`p2p-cid`, token.Cid)
	header.Add(`p2p-aid`, token.Aid)
	header.Add(`p2p-pid`, data.SelfP2PID) //libp2p id
	header.Add(`p2p-on`, osName)
	header.Add(`p2p-cp`, fmt.Sprintf("%d", data.ControlPort))
	c, _, err := websocket.DefaultDialer.Dial(u.String(), header)
	serverConn = &data.Connetion{Con: c, Header: header, Address: u.String()}
	if err != nil {
		MSG.SendMessage(fmt.Sprintf("p2p socket 连接[%s]失败:%s", u.String(), err.Error()))
		SocketRun = false
		return
	}
	MSG.SendMessage(fmt.Sprintf("p2p socket 连接服务成功:%s", u.String()))
	// go utils.DelayExe(time.Second*60*3, func() {
	// 	serverConn.Con.Close()
	// })

	ticker := time.NewTicker(pingPeriod)
	defer func() {
		msg.SendMessage(fmt.Sprintf(`conn关闭%d`, token.Cid))
		SocketRun = false
		ticker.Stop()
		// serverConn = nil
	}()
	for {
		select {
		case <-ticker.C:
			if serverConn != nil {
				fmt.Println("SendJsonToMaster2 func")
				if err := SendJsonToMaster(data.P2PSocketParams{MType: "ping"}, true); err != nil {
					MSG.SendMessage(fmt.Sprintf("p2p socket ping server 失败: %s", err.Error()))
					restartOk := false
					for i := 3; i > 0; i-- {
						c, _, err := websocket.DefaultDialer.Dial(u.String(), serverConn.Header)
						if err != nil {
							MSG.SendMessage(fmt.Sprintf("重连socket发生错误, %s", err.Error()))
						} else {
							serverConn.Con = c
							restartOk = true
							break
						}
					}

					if !restartOk {
						//close(done) // 关闭通道通知退出
						return
					}
				}
			}
		case <-ctx.Done():
			ticker.Stop()
			return
		}
	}
}

func handlerRelayInfo(params *data.P2PSocketParams) {
	if len(params.Relay.Id) == 0 {
		MSG.SendMessage("未收到relay Id信息")
		return
	}
	if len(params.Relay.Addr) == 0 {
		MSG.SendMessage("未收到relay addr信息")
		return
	}
	RelayId = params.Relay.Id
	RelayAddr = params.Relay.Addr
	if err := p2p.SetRelayServer(RelayId, RelayAddr); err != nil {
		msg.ErrCheck(fmt.Sprintf("%s", err.Error()))
	}
}

func handlerPeer(params *data.P2PSocketParams) {
	if len(params.My.IIp) == 0 {
		MSG.SendMessage("未收到本节点信息")
		return
	}
	data.SelfClientName = params.My.Name
	data.SelfIp = params.My.IIp
	MSG.SendMessage("处理 peers")
	// needCheck := false

	for _, _peer := range params.Peers {
		peer := _peer
		if len(peer.Id) == 0 || len(peer.IIp) == 0 {
			MSG.SendMessage(fmt.Sprintf("peer[%s]参数不完整，跳过", peer.Id))
			continue
		}
		value, ok := data.LocalPeers.Load(peer.Cid)
		if ok {
			localPeer := value.(*data.LocalPeer)
			if localPeer.LibP2PId != peer.Id {
				p2p.Down(localPeer.LibP2PId, localPeer.InnerIp)
				p2p.Up(peer.Id, peer.IIp)
				localPeer.Status = 1
				localPeer.LibP2PId = peer.Id
				localPeer.ConfigUpdateTime = time.Now()
				data.LocalPeers.Store(peer.Cid, localPeer)
				MSG.SendMessage(fmt.Sprintf("peer [%s]配置更新成功, p2pid: %s mode: %s", peer.Cid, peer.Id))

			}
			if localPeer.Status == 4 {
				p2p.Up(peer.Id, peer.IIp)
				localPeer.Status = 1
				localPeer.LibP2PId = peer.Id
				localPeer.ConfigUpdateTime = time.Now()
				data.LocalPeers.Store(peer.Cid, localPeer)
				MSG.SendMessage(fmt.Sprintf("peer [%s]重新上线, p2pid: %s mode: %s", peer.Cid, peer.Id))
			}
		} else {
			p2p.Up(peer.Id, peer.IIp)
			localpeer := &data.LocalPeer{
				Name:          peer.Name,
				Status:        1,
				InnerIp:       peer.IIp,
				ConfigAddTime: time.Now(),
				OsName:        peer.On,
				ControlPort:   peer.Cp,
				FailCount:     0,
				LibP2PId:      peer.Id,
				ClientId:      peer.Cid,
			}
			data.LocalPeers.Store(peer.Cid, localpeer)
			MSG.SendMessage(fmt.Sprintf("peer [%s]添加成功, p2pid: %s mode: %s", peer.Cid, peer.Id))
		}
	}

	data.LocalPeers.Range(func(key, value any) bool {
		cid := key.(string)
		localPeer := value.(*data.LocalPeer)
		exists := false
		for _, peer := range params.Peers {
			if cid == peer.Cid {
				exists = true
				break
			}
		}

		if !exists {
			MSG.SendMessage(fmt.Sprintf("peer [%s]离线处理", cid))
			p2p.Down(localPeer.LibP2PId, localPeer.InnerIp)
			localPeer.ConfigDelTime = time.Now()
			localPeer.Status = 4
			data.LocalPeers.Store(cid, localPeer)
		}

		return true
	})

}

func decrypt(encryptStr, key string) (*data.P2PSocketParams, error) {
	if len(encryptStr) != 0 {
		jsonStr, err := utils.AesDecryptByKey(encryptStr, key)
		if err != nil {
			MSG.SendMessage(fmt.Sprintf("p2p socket 解密失败:%s", err))
			return nil, err
		}
		MSG.SendMessage(fmt.Sprintf("解密后[%s]", jsonStr))
		params := data.P2PSocketParams{}
		err = json.Unmarshal([]byte(jsonStr), &params)
		if err != nil {
			MSG.SendMessage(fmt.Sprintf("p2p socket 解密后[%s]转json失败:%s", []byte(jsonStr), err))
			return nil, err
		}
		return &params, nil
	}
	return nil, nil
}

func SendJsonToMaster(data data.P2PSocketParams, logSw bool) error {
	if serverConn == nil {
		return fmt.Errorf(`与server未建立socket连接`)
	}
	enData, err := encryptionDataByP2p(utils.DefaultAesKey, data)
	if err != nil {
		return err
	}
	serverConn.Con.SetWriteDeadline(time.Now().Add(writeWait))
	serverConn.Mutex.Lock()
	err = serverConn.Con.WriteJSON(&enData)
	serverConn.Mutex.Unlock()
	if err != nil {
		return fmt.Errorf(`发送json失败:%s`, err.Error())
	}
	if logSw {
		MSG.SendMessage(fmt.Sprintf(`socket 发送json成功,原始数据:%s`, data))
	}
	return nil
}

func encryptionDataByP2p(key string, datas data.P2PSocketParams) (interface{}, error) {
	if len(key) != 0 {
		str, err := json.Marshal(datas)
		if err != nil {
			MSG.SendMessage(fmt.Sprintf(`p2p socket 发送json 对象转json失败:%s`, err.Error()))
			return nil, err
		}
		encryptStr, err := utils.AesEncryptByKey(string(str), key)
		if err != nil {
			msg.SendMessage(fmt.Sprintf(`p2p socket 发送json 数据加密失败:%s`, err.Error()))
			return nil, err
		}
		return data.TransmitParams{EncryptionData: encryptStr}, nil
	}
	return datas, nil
}
